﻿using System;
using System.ComponentModel;
using System.ComponentModel.Composition;
using Caliburn.Micro;
using Zero.Common;

namespace Zero.Frontend
{
    /// <summary>
    /// Sends request to backend asynchronously as part of coroutine.
    /// </summary>
    /// <typeparam name="TResult"></typeparam>
    public class BackendResult<TResult> : IResult
    {
        private readonly IRequest<TResult> _request;
        private readonly bool _catchException;

        /// <summary>
        /// Backend to call. Resolved through MEF.
        /// </summary>
        [Import]
        public ITypedBackend Backend { get; set; }

        /// <summary>
        /// Iniciliazes this result with specific request.
        /// </summary>
        /// <param name="request">Transport object to send as request.</param>
        /// <param name="catchException"><c>true</c> if this result should catch exceptions and present it as <see cref="Error"/>. <c>false</c> if exceptions should be left to coroutine.</param>
        public BackendResult(IRequest<TResult> request, bool catchException)
        {
            _request = request;
            _catchException = catchException;
        }

        /// <summary>
        /// Result of request. Only valid after execution and when <see cref="Successful"/> equals <true />.
        /// </summary>
        public TResult Result { get; private set; }

        /// <summary>
        /// Error that occured during request.
        /// </summary>
        public Exception Error { get; private set; }

        /// <summary>
        /// <c>true</c> if no error ocured during request.
        /// </summary>
        public bool Successful
        {
            get { return Error == null; }
        }

        void IResult.Execute(ActionExecutionContext context)
        {
            BackgroundWorker bw = new BackgroundWorker();
            
            bw.DoWork += (sender, e) => { Result = Backend.Process(_request); };
            bw.RunWorkerCompleted += bw_RunWorkerCompleted;

            bw.RunWorkerAsync();
        }

        void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (_catchException)
            {
                if (e.Error != null)
                    Error = e.Error;

                _completed(this, new ResultCompletionEventArgs() { WasCancelled = e.Cancelled });
            }
            else
            {
                _completed(this, new ResultCompletionEventArgs() { Error = e.Error, WasCancelled = e.Cancelled });
            }
        }

        private EventHandler<ResultCompletionEventArgs> _completed = delegate { };
        event EventHandler<ResultCompletionEventArgs> IResult.Completed
        {
            add { _completed += value; }
            remove { _completed -= value; }
        }
    }
}
    